#include "Dri_I2C.h"

/**
 * @brief I2C 初始化
 *
 */
void Dri_I2C_Init(void)
{
    // 1. 对 GPIOB 时钟使能
    RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

    // 2. SCL（PB10）设置为通用开漏输出，MODE=11,CNF=01
    GPIOB->CRH |= GPIO_CRH_MODE10;
    GPIOB->CRH &= ~GPIO_CRH_CNF10_1;
    GPIOB->CRH |= GPIO_CRH_CNF10_0;

    // 3. SDA（PB11）设置为通用开漏输出
    GPIOB->CRH |= GPIO_CRH_MODE11;
    GPIOB->CRH &= ~GPIO_CRH_CNF11_1;
    GPIOB->CRH |= GPIO_CRH_CNF11_0;
}

/**
 * @brief 发送起始信号
 *
 */
void Dri_I2C_Start(void)
{
    // 拉高 SCL、SDA，处于空闲状态
    SCL_HIGH;
    SDA_HIGH;

    // 延时
    SCL_DELAY;

    // 拉低SDA，以产生一个起始信号，并延时
    SDA_LOW;
    SCL_DELAY;

    // 拉低SCL，并延时
    SCL_LOW;
    SCL_DELAY;
}

/**
 * @brief 发送停止信号
 *
 */
void Dri_I2C_Stop(void)
{
    // 拉低SDA，并延时
    SDA_LOW;
    SCL_DELAY;

    // 拉高SCL
    SCL_HIGH;
    SCL_DELAY;
   
    // 拉高SDA，以产生结束信号
    SDA_HIGH;
    SCL_DELAY;
}

/**
 * @brief 发送一个字节的数据
 * 
 * @param byte 要发送的字节
 */
void Dri_I2C_TransmitByte(uint8_t byte)
{
    // 循环，逐bit发送, 先发高位
    for (uint8_t i = 0; i < 8; i++)
    {
        // 判断byte当前的最高位是否是1, byte & 0b10000000
        if (byte & 0x80)
        {
            // 拉高SDA
            SDA_HIGH;
        }
        else
        {
            // 拉低SDA
            SDA_LOW;
        }

        // byte 左移一位，将已发送的数据移出，待发送的bit进入最高位
        byte <<= 1;

        // 拉高SCL，让从设备进行采样，并延时
        SCL_HIGH;
        SCL_DELAY;

        // 拉低SCL，并延时
        SCL_LOW;
        SCL_DELAY;
    }
}

/**
 * @brief 接收一个应答信号
 * 
 * @return uint8_t 接收到的应答信号，1表示NACK，0表示ACK
 */
uint8_t Dri_I2C_ReceiveACK(void)
{
    // 主设备释放数据总线
    SDA_HIGH;

    // 拉高SCL,读取SDA数据，并延时
    SCL_HIGH;
    uint8_t ack = SDA_READ ? 1 : 0;
    SCL_DELAY;

    // 拉低SCL，并延时
    SCL_LOW;
    SCL_DELAY;

    // 返回应答信号
    return ack;
}

/**
 * @brief 接收一个字节的数据
 * 
 * @return uint8_t 接收到的字节
 */
uint8_t Dri_I2C_ReceiveByte(void)
{
    // 主设备释放数据总线
    SDA_HIGH;

    // 定义变量，用于接收数据
    uint8_t byte = 0x00;

    // 逐bit接收数据，先接受高位
    for (uint8_t i = 0; i < 8; i++)
    {
        // 拉高SCL，进行采样
        SCL_HIGH;

        // byte 左移一位，将最低位空出，用于保存SDA读取的数据
        byte <<= 1;

        // 读取SDA数据
        if (SDA_READ)
        {
            byte |= 0x01;      // 将byte 当前最低位置1
        }

        // 延时
        SCL_DELAY;

        // SCL拉低并延时
        SCL_LOW;
        SCL_DELAY;
    }

    // 返回
    return byte;
}

/**
 * @brief 发送ACK信号
 * 
 * @param ack 要发送的ACK信号
 */
void Dri_I2C_TransmitACK(uint8_t ack)
{
    // 根据 ack 修改 SDA 状态
    if (ack)
    {
        SDA_HIGH;
    }
    else
    {
        SDA_LOW;
    }

    // 拉高 SCL，让从设备采样，并延时
    SCL_HIGH;
    SCL_DELAY;

    // 拉低SCL,并延时
    SCL_LOW;
    SCL_DELAY;
}
